Default, Named, and Parameterized Constructors in Dart
Constructors are an important part of Object-Oriented Programming (OOP) in Dart. A constructor is a special member of a class that is used to create and initialize objects. Constructors are especially important in Flutter because Flutter applications are built around classes and objects.
JustAcademy's Flutter curriculum includes Dart programming fundamentals, including Object-Oriented Programming, classes, objects, and constructors. :contentReference[oaicite:0]{index=0}
Flutter Training: https://www.justacademy.co/course-detail/flutter-training
Course Demo Registration: https://www.justacademy.co/register-for-course-demo
1. What is a Constructor?
A constructor is a special function associated with a class that is called when an object of that class is created. Constructors are commonly used to initialize the properties of an object.
For example:
class Student {
String name = '';
Student() {
print('Student object created');
}
}
void main() {
Student student = Student();
}
Output:
Student object created
In this example, Student() is the constructor of the Student class. It executes when a Student object is created.
2. Why Are Constructors Used?
Constructors are mainly used to:
- Initialize object properties.
- Set default values.
- Receive values when creating an object.
- Provide different ways to create objects.
- Perform initialization logic.
- Create objects with meaningful and valid initial states.
- Make classes easier to use and maintain.
3. Types of Constructors in Dart
Dart provides several constructor patterns. The three fundamental constructor types covered in this topic are:
- Default Constructor
- Parameterized Constructor
- Named Constructor
Dart also supports other useful constructor features such as named parameters, initializer lists, redirecting constructors, const constructors, and factory constructors.
4. Default Constructor
A default constructor is a constructor that can create an object without requiring constructor arguments.
If a class does not declare any constructor, Dart provides an implicit default constructor for the class.
Example: Implicit Default Constructor
class Student {
String name = 'Rahul';
int age = 20;
}
void main() {
Student student = Student();
print(student.name);
print(student.age);
}
Output:
Rahul
20
Here, Student() can be used to create the object because the class has no explicitly declared constructor.
5. Explicit Default Constructor
You can also explicitly define a constructor that takes no arguments.
class Student {
String name = 'Rahul';
int age = 20;
Student() {
print('Student object created');
}
}
void main() {
Student student = Student();
print(student.name);
print(student.age);
}
The constructor Student() does not receive any arguments, so it is a no-argument/default-style constructor.
6. Default Values with a Constructor
A constructor can initialize properties with predefined values.
class Product {
String name;
double price;
Product()
: name = 'Unknown Product',
price = 0.0;
}
void main() {
Product product = Product();
print(product.name);
print(product.price);
}
The initializer list assigns values before the constructor body runs.
7. Parameterized Constructor
A parameterized constructor accepts values when an object is created. This allows every object to be initialized with different data.
class Student {
String name;
int age;
Student(String name, int age)
: this.name = name,
this.age = age;
}
void main() {
Student student = Student('Rahul', 21);
print(student.name);
print(student.age);
}
Output:
Rahul
21
Here:
String name receives the student's name.
int age receives the student's age.
- The values are assigned to the object's properties.
8. Using this in a Parameterized Constructor
Dart provides a shorthand syntax for assigning constructor parameters to instance fields.
class Student {
String name;
int age;
Student(this.name, this.age);
}
void main() {
Student student = Student('Aman', 22);
print(student.name);
print(student.age);
}
The following two styles are conceptually equivalent:
Traditional Style
class Student {
String name;
int age;
Student(String name, int age)
: name = name,
age = age;
}
Shorthand Style
class Student {
String name;
int age;
Student(this.name, this.age);
}
The second form is commonly used because it is shorter and easier to read.
9. Multiple Objects Using a Parameterized Constructor
class Student {
String name;
int age;
Student(this.name, this.age);
void display() {
print('Name: $name');
print('Age: $age');
}
}
void main() {
Student student1 = Student('Rahul', 20);
Student student2 = Student('Priya', 22);
Student student3 = Student('Amit', 21);
student1.display();
student2.display();
student3.display();
}
The same constructor can create multiple objects with different values.
10. Named Constructor
A named constructor is a constructor with an additional name that provides an alternative way to create an object.
Named constructors are useful when a class needs multiple meaningful ways to initialize an object.
class Student {
String name;
int age;
Student(this.name, this.age);
Student.guest()
: name = 'Guest',
age = 0;
}
void main() {
Student student1 = Student('Rahul', 21);
Student student2 = Student.guest();
print(student1.name);
print(student2.name);
}
Output:
Rahul
Guest
Here, the class has two constructors:
Student(this.name, this.age)
Student.guest()
11. Why Use Named Constructors?
Named constructors make the purpose of object creation more explicit.
For example:
Student.guest();
clearly communicates that a guest/default student object is being created.
Another example:
Product.fromJson(data);
clearly indicates that the object is being created from JSON data.
12. Multiple Named Constructors
Dart allows a class to define multiple named constructors.
class User {
String name;
String role;
User(this.name, this.role);
User.admin()
: name = 'Admin User',
role = 'Administrator';
User.guest()
: name = 'Guest User',
role = 'Guest';
User.customer()
: name = 'Customer User',
role = 'Customer';
}
void main() {
User admin = User.admin();
User guest = User.guest();
User customer = User.customer();
print(admin.role);
print(guest.role);
print(customer.role);
}
Output:
Administrator
Guest
Customer
13. Parameterized Named Constructor
A named constructor can also accept parameters.
class Product {
String name;
double price;
Product(this.name, this.price);
Product.discounted(this.name, this.price) {
price = price * 0.90;
}
}
void main() {
Product product = Product.discounted('Laptop', 50000);
print(product.name);
print(product.price);
}
Here, Product.discounted() is a named constructor that receives parameters and applies a discount during object creation.
14. Named Parameters vs Named Constructors
These two concepts have different meanings.
Named Parameters
Named parameters are parameters identified by their names when calling a constructor or function.
class Student {
String name;
int age;
Student({
required this.name,
required this.age,
});
}
void main() {
Student student = Student(
name: 'Rahul',
age: 21,
);
}
Named Constructor
A named constructor gives the constructor itself an additional name.
class Student {
String name;
int age;
Student(this.name, this.age);
Student.guest()
: name = 'Guest',
age = 0;
}
Therefore:
- Named parameter:
name:, age:
- Named constructor:
Student.guest()
15. Required Named Parameters
Dart supports named parameters with the required keyword.
class Employee {
String name;
double salary;
Employee({
required this.name,
required this.salary,
});
}
void main() {
Employee employee = Employee(
name: 'Rahul',
salary: 50000,
);
print(employee.name);
print(employee.salary);
}
The caller must provide both name and salary.
16. Optional Named Parameters
Named parameters can also have default values.
class User {
String name;
String role;
User({
required this.name,
this.role = 'User',
});
}
void main() {
User user1 = User(name: 'Rahul');
User user2 = User(
name: 'Priya',
role: 'Admin',
);
print(user1.role);
print(user2.role);
}
Output:
User
Admin
17. Optional Positional Constructor Parameters
Dart also supports optional positional parameters.
class Student {
String name;
int age;
Student(this.name, [this.age = 18]);
}
void main() {
Student student1 = Student('Rahul');
Student student2 = Student('Priya', 22);
print(student1.age);
print(student2.age);
}
The age parameter is optional and has a default value of 18.
18. Initializer List
An initializer list allows fields to be initialized before the constructor body executes.
class Rectangle {
double width;
double height;
double area;
Rectangle(this.width, this.height)
: area = width * height;
}
void main() {
Rectangle rectangle = Rectangle(10, 5);
print(rectangle.area);
}
Output:
50
The expression width * height is evaluated in the initializer list before the constructor body runs.
19. Constructor with Validation
Constructors can also validate values during object creation.
class BankAccount {
String accountHolder;
double balance;
BankAccount(this.accountHolder, this.balance)
: assert(balance >= 0);
}
void main() {
BankAccount account = BankAccount('Rahul', 10000);
print(account.accountHolder);
print(account.balance);
}
The assertion expresses the rule that the initial balance should not be negative. Assertions are primarily useful during development and debugging.
20. Named Constructor for JSON Data
Named constructors are frequently useful for creating model objects from external data such as JSON.
class User {
String name;
String email;
User(this.name, this.email);
User.fromJson(Map json)
: name = json['name'],
email = json['email'];
}
void main() {
Map data = {
'name': 'Rahul',
'email': '[email protected]',
};
User user = User.fromJson(data);
print(user.name);
print(user.email);
}
fromJson() clearly communicates that the object is being constructed from JSON-like map data.
21. Constructor Example for an E-Commerce Product
class Product {
String name;
double price;
String category;
Product({
required this.name,
required this.price,
required this.category,
});
Product.electronics({
required this.name,
required this.price,
}) : category = 'Electronics';
Product.clothing({
required this.name,
required this.price,
}) : category = 'Clothing';
}
void main() {
Product laptop = Product.electronics(
name: 'Laptop',
price: 55000,
);
Product shirt = Product.clothing(
name: 'Shirt',
price: 1500,
);
print(laptop.category);
print(shirt.category);
}
Multiple named constructors can provide meaningful creation paths for different product categories.
22. Redirecting Constructors
A constructor can redirect to another constructor in the same class.
class Student {
String name;
int age;
Student(this.name, this.age);
Student.guest() : this('Guest', 0);
}
void main() {
Student student = Student.guest();
print(student.name);
print(student.age);
}
Here, Student.guest() redirects to the main Student() constructor.
23. Const Constructors
Dart supports const constructors for classes whose objects can be created as compile-time constants.
class Point {
final int x;
final int y;
const Point(this.x, this.y);
}
void main() {
const Point point = Point(10, 20);
print(point.x);
print(point.y);
}
A const constructor is useful when an object is immutable and can be created as a compile-time constant.
24. Factory Constructors
Dart also provides factory constructors. Unlike a generative constructor, a factory constructor can return an existing instance or an instance of a subtype and does not necessarily create a new object every time it is called.
class Logger {
static final Logger _instance = Logger._internal();
factory Logger() {
return _instance;
}
Logger._internal();
void log(String message) {
print(message);
}
}
void main() {
Logger logger1 = Logger();
Logger logger2 = Logger();
print(identical(logger1, logger2));
}
Factory constructors are useful for patterns such as caching, object selection, or controlled instance creation.
25. Private Named Constructor
Dart supports library-private identifiers using a leading underscore. A private constructor can be used to control how instances are created within a library.
class Database {
Database._internal();
static final Database instance = Database._internal();
}
void main() {
Database database = Database.instance;
print(database);
}
The private constructor prevents normal construction through Database._internal() from outside the library.
26. Final Fields and Constructors
A final instance field must be initialized before the object is fully constructed. Constructors are commonly used to initialize such fields.
class Employee {
final String name;
final double salary;
Employee(this.name, this.salary);
}
void main() {
Employee employee = Employee('Rahul', 60000);
print(employee.name);
print(employee.salary);
}
After initialization, the values of the final fields cannot be reassigned.
27. Constructors and Inheritance
Constructors are not inherited by subclasses. A child class must define its own constructors and can initialize the parent class through super.
class Person {
String name;
Person(this.name);
}
class Student extends Person {
int age;
Student(String name, this.age) : super(name);
}
void main() {
Student student = Student('Rahul', 21);
print(student.name);
print(student.age);
}
Here, super(name) calls the constructor of the parent class.
28. Constructor Initialization Order
When a subclass object is created, initialization involves the superclass constructor and the subclass initialization process.
class Parent {
Parent() {
print('Parent constructor');
}
}
class Child extends Parent {
Child() {
print('Child constructor');
}
}
void main() {
Child child = Child();
}
Output:
Parent constructor
Child constructor
The superclass constructor is executed before the subclass constructor body.
29. Constructors in Flutter Widgets
Constructors are used extensively in Flutter widgets to receive configuration values.
import 'package:flutter/material.dart';
class WelcomeCard extends StatelessWidget {
final String title;
final String subtitle;
const WelcomeCard({
super.key,
required this.title,
required this.subtitle,
});
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Text(title),
Text(subtitle),
],
),
),
);
}
}
The constructor receives title and subtitle and stores them in final fields.
This is a common Flutter pattern: immutable widget configuration is passed through the constructor.
30. Creating a Flutter Widget Using Its Constructor
WelcomeCard(
title: 'Welcome',
subtitle: 'Learn Flutter with Dart',
)
Named parameters make Flutter widget constructors easy to read and understand.
31. Default vs Parameterized vs Named Constructor
| Feature |
Default Constructor |
Parameterized Constructor |
Named Constructor |
| Arguments |
Normally no required arguments |
Accepts arguments |
May accept arguments |
| Purpose |
Basic object creation |
Initialize object with supplied values |
Provide alternative named creation paths |
| Example |
Student() |
Student('Rahul', 21) |
Student.guest() |
| Multiple versions |
Only one unnamed constructor |
Can use named constructors for alternatives |
Multiple named constructors can exist |
| Common Use |
Simple initialization |
Object-specific data |
Different creation scenarios |
32. Default, Named, and Parameterized Constructor Together
A class can provide different creation options using a combination of constructor features.
class User {
String name;
String role;
int age;
User(this.name, this.role, this.age);
User.guest()
: name = 'Guest',
role = 'Guest',
age = 0;
User.admin(String name)
: this(name, 'Admin', 30);
}
void main() {
User user1 = User('Rahul', 'Customer', 25);
User user2 = User.guest();
User user3 = User.admin('Priya');
print(user1.role);
print(user2.role);
print(user3.role);
}
This example demonstrates:
- A parameterized unnamed constructor.
- A named constructor for a guest user.
- A named constructor that redirects to the main constructor.
33. Practical Bank Account Example
class BankAccount {
String accountHolder;
double balance;
BankAccount(this.accountHolder, this.balance);
BankAccount.zeroBalance(this.accountHolder)
: balance = 0;
BankAccount.savings({
required this.accountHolder,
required this.balance,
});
void display() {
print('Account Holder: $accountHolder');
print('Balance: $balance');
}
}
void main() {
BankAccount account1 = BankAccount('Rahul', 10000);
BankAccount account2 = BankAccount.zeroBalance('Amit');
BankAccount account3 = BankAccount.savings(
accountHolder: 'Priya',
balance: 25000,
);
account1.display();
account2.display();
account3.display();
}
This demonstrates how different constructors can represent different object-creation scenarios.
34. Practical Student Management Example
class Student {
String name;
int age;
String course;
Student({
required this.name,
required this.age,
required this.course,
});
Student.guest()
: name = 'Guest Student',
age = 0,
course = 'Not Assigned';
Student.fromExisting({
required this.name,
required this.age,
}) : course = 'Flutter';
void display() {
print('Name: $name');
print('Age: $age');
print('Course: $course');
}
}
void main() {
Student student1 = Student(
name: 'Rahul',
age: 21,
course: 'Flutter',
);
Student student2 = Student.guest();
Student student3 = Student.fromExisting(
name: 'Priya',
age: 22,
);
student1.display();
student2.display();
student3.display();
}
35. Common Mistakes with Constructors
Mistake 1: Forgetting Required Initialization
Non-nullable fields generally need to be initialized before construction completes. Constructors are one of the primary ways to ensure this.
Mistake 2: Confusing Named Parameters with Named Constructors
Student(
name: 'Rahul',
);
Here, name: is a named parameter.
Student.guest();
Here, guest is part of the named constructor.
Mistake 3: Creating Too Many Constructors
Constructors should have meaningful purposes. Avoid adding constructors that provide nearly identical creation behavior without a clear reason.
Mistake 4: Not Using this Shorthand
For simple field assignments, Dart's this.field constructor parameter syntax can make code shorter and easier to read.
36. Constructor Best Practices
- Use constructors to create valid initial object states.
- Use
required named parameters for values that must be supplied.
- Use named constructors when a class has multiple meaningful creation paths.
- Use
this.field shorthand for straightforward field initialization.
- Use initializer lists when fields need calculated or validated initialization.
- Use
const constructors for suitable immutable classes.
- Use factory constructors when object creation requires custom logic or controlled instance selection.
- Keep constructor APIs clear and easy to understand.
- In Flutter widgets, prefer immutable
final fields and constructor parameters for configuration.
37. Quick Revision
| Concept |
Meaning |
Example |
| Constructor |
Used to initialize an object. |
Student() |
| Default Constructor |
Creates an object without required constructor arguments. |
Student() |
| Parameterized Constructor |
Receives values during object creation. |
Student('Rahul', 21) |
| Named Constructor |
Provides an alternative named way to construct an object. |
Student.guest() |
| Named Parameter |
Parameter supplied using its parameter name. |
name: 'Rahul' |
| Initializer List |
Initializes fields before the constructor body executes. |
: area = width * height |
| Redirecting Constructor |
Redirects construction to another constructor. |
: this(...) |
| Const Constructor |
Supports compile-time constant object creation. |
const Point(...) |
| Factory Constructor |
Can control which instance is returned. |
factory Logger() |
38. Practice Exercises
- Create a
Student class with a default constructor.
- Create a parameterized constructor that accepts student name and age.
- Create a named constructor called
Student.guest().
- Create a
Product class with a parameterized constructor.
- Create
Product.electronics() and Product.clothing() named constructors.
- Create an
Employee class using required named parameters.
- Create a
User.fromJson() named constructor.
- Create a
Rectangle class that calculates its area using an initializer list.
- Create an immutable
Point class with a const constructor.
- Create a Flutter
StatelessWidget with required constructor parameters.
39. Key Takeaways
- A constructor is used to initialize an object when it is created.
- A default/no-argument constructor creates an object without required constructor arguments.
- A parameterized constructor receives values during object creation.
- A named constructor provides an additional named way to create an object.
- Named parameters and named constructors are different concepts.
- The
this keyword is commonly used for concise field initialization.
- Initializer lists are useful for calculated and validated initialization.
- Redirecting constructors can reuse another constructor's initialization logic.
- Const constructors are useful for suitable immutable classes.
- Factory constructors provide additional control over object creation.
- Constructors are heavily used in Flutter widgets to pass configuration data.
- Understanding constructors is essential for working effectively with Dart OOP and Flutter.
40. JustAcademy Flutter Learning Resources
JustAcademy's published Flutter curriculum includes Dart OOP topics such as classes, objects, constructors, inheritance, polymorphism, and abstraction. :contentReference[oaicite:1]{index=1}
Flutter Training Course:
https://www.justacademy.co/course-detail/flutter-training
Register for Course Demo:
https://www.justacademy.co/register-for-course-demo